#[derive(Debug, Clone)]
struct Context {
    id: u32,
    name: String,
    url: String,
    payload: Vec<u8>,
}

trait FromContext {
    fn from_context(context: &Context) -> Self;
}

trait Handler<T> {
    fn call(self, context: Context);
}

// impl<F, T> Handler<T> for F
// where
//     F: Fn(T),
//     T: FromContext,
// {
//     fn call(self, context: Context) {
//         self(T::from_context(&context));
//     }
// }

macro_rules! all_the_tuples {
    ($name:ident) => {
        $name!([]);
        $name!([T1]);
        $name!([T1, T2]);
        $name!([T1, T2, T3]);
        $name!([T1, T2, T3, T4]);
        $name!([T1, T2, T3, T4, T5]);
        $name!([T1, T2, T3, T4, T5, T6]);
        $name!([T1, T2, T3, T4, T5, T6, T7]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]);
        $name!([
            T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
        ]);
        $name!([
            T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
        ]);
    };
}

macro_rules! impl_handler {
    (
        [$($ty:ident),*]
    ) => {
        impl<F, $($ty,)*> Handler<($($ty,)*)> for F
        where
            F: Fn($($ty,)*),
            $( $ty: FromContext,)*
        {
            fn call(self, context: Context) {
                self($( $ty::from_context(&context), )*);
            }
        }
    };
}

all_the_tuples!(impl_handler);

fn trigger<T, H>(context: Context, handler: H)
where
    H: Handler<T>,
{
    handler.call(context);
}

struct Params(String);

impl FromContext for Params {
    fn from_context(context: &Context) -> Self {
        Params(context.name.clone())
    }
}

struct Id(u32);

impl FromContext for Id {
    fn from_context(context: &Context) -> Self {
        Id(context.id)
    }
}

fn hello() {
    println!("hello start");
}

fn hello2(Params(name): Params, Id(id): Id) {
    println!("hello {}, your id:{}", name, id);
}

#[cfg(test)]
mod test {
    use super::{Context, hello, hello2, trigger};

    #[test]
    fn trigger_test() {
        let context = Context {
            id: 1,
            name: "LoongWang".to_string(),
            url: "easy8in.top".to_string(),
            payload: Vec::new(),
        };

        trigger(context.clone(), hello);
        trigger(context.clone(), hello2);
    }
}
